interface MatrixElement<T = any> {
    x: number;
    y: number;
    val: T;
}

type Direction = 'up' | 'down' | 'left' | 'right' | 'up-left' | 'up-right' | 'down-left' | 'down-right';

export class SimpleMatrix<T = any> {
    private matrix: MatrixElement<T>[][];
    private returnFullInfo: boolean = true;

    constructor(arr: T[][]) {
        this.matrix = arr.map((row, i) => 
            row.map((val, j) => ({
                x: i,
                y: j,
                val: val
            }))
        );
    }

    getMatrix(): MatrixElement<T>[][] {
        return this.matrix;
    }

    getCol(col: number, f: boolean = this.returnFullInfo): MatrixElement<T>[] | T[] {
        this.checkCol(col);
        const newArr: (MatrixElement<T> | T)[] = [];
        
        for (let i = 0; i < this.matrix.length; i++) {
            for (let j = 0; j < this.matrix[i].length; j++) {
                if (j === col) {
                    newArr.push(f ? this.matrix[i][j] : this.matrix[i][j].val);
                }
            }
        }
        
        return newArr as MatrixElement<T>[] | T[];
    }

    getRow(row: number, f: boolean = this.returnFullInfo): MatrixElement<T>[] | T[] {
        this.checkRow(row);
        const newArr: (MatrixElement<T> | T)[] = [];
        
        for (let i = 0; i < this.matrix.length; i++) {
            for (let j = 0; j < this.matrix[i].length; j++) {
                if (i === row) {
                    newArr.push(f ? this.matrix[i][j] : this.matrix[i][j].val);
                }
            }
        }
        
        return newArr as MatrixElement<T>[] | T[];
    }

    getRound([row, col]: [number, number], f: boolean = this.returnFullInfo): (MatrixElement<T> | T)[] {
        this.checkAll([row, col]);
        const result: (MatrixElement<T> | T)[] = [];

        for (let i = row - 1; i <= row + 1; i++) {
            for (let j = col - 1; j <= col + 1; j++) {
                const isValid = i >= 0 && 
                              i < this.matrix.length && 
                              j >= 0 && 
                              j < this.matrix[0].length &&
                              !(i === row && j === col);
                              
                if (isValid) {
                    const item = this.matrix[i][j];
                    result.push(f ? item : item.val);
                }
            }
        }

        return result;
    }

    getRelativeElement(
        [row, col]: [number, number], 
        direction: Direction, 
        steps: number = 1, 
        f: boolean = this.returnFullInfo
    ): MatrixElement<T> | T | undefined {
        this.checkAll([row, col]);
        
        if (steps < 1) {
            throw new Error("Steps must be a positive integer");
        }

        let targetRow = row;
        let targetCol = col;

        switch(direction.toLowerCase() as Direction) {
            case 'up':
                targetRow -= steps;
                break;
            case 'down':
                targetRow += steps;
                break;
            case 'left':
                targetCol -= steps;
                break;
            case 'right':
                targetCol += steps;
                break;
            case 'up-left':
                targetRow -= steps;
                targetCol -= steps;
                break;
            case 'up-right':
                targetRow -= steps;
                targetCol += steps;
                break;
            case 'down-left':
                targetRow += steps;
                targetCol -= steps;
                break;
            case 'down-right':
                targetRow += steps;
                targetCol += steps;
                break;
            default:
                throw new Error("Invalid direction");
        }

        const isValid = targetRow >= 0 && 
                       targetRow < this.matrix.length && 
                       targetCol >= 0 && 
                       targetCol < this.matrix[0].length;

        if (!isValid) {
            return undefined;
        }

        return f ? this.matrix[targetRow][targetCol] : this.matrix[targetRow][targetCol].val;
    }

    private checkCol(v: number): void {
        if (v >= this.matrix[0].length || v < 0) {
            throw new Error("SimpleMatrixJS: Column index out of bounds");
        }
    }
    
    private checkRow(v: number): void {
        if (v >= this.matrix.length || v < 0) {
            throw new Error("SimpleMatrixJS: Row index out of bounds");
        }
    }
    
    private checkAll([row, col]: [number, number]): void {
        this.checkRow(row);
        this.checkCol(col);
    }

    print(matrix: MatrixElement<T>[][]): void {
        let output = '\n';
        const colWidths: number[] = [];
        
        for(let j = 0; j < matrix[0].length; j++) {
            let maxWidth = 0;
            for(let i = 0; i < matrix.length; i++) {
                maxWidth = Math.max(maxWidth, String(matrix[i][j].val).length);
            }
            colWidths[j] = maxWidth;
        }
        
        output += '┌';
        for(let j = 0; j < matrix[0].length; j++) {
            output += '─'.repeat(colWidths[j] + 2);
            if(j < matrix[0].length - 1) output += '┬';
        }
        output += '┐\n';
        
        for(let i = 0; i < matrix.length; i++) {
            output += '│';
            for(let j = 0; j < matrix[i].length; j++) {
                const val = String(matrix[i][j].val);
                const padding = ' '.repeat(colWidths[j] - val.length);
                output += ' ' + val + padding + ' ';
                if(j < matrix[i].length - 1) output += '│';
            }
            output += '│\n';
            
            if(i < matrix.length - 1) {
                output += '├';
                for(let j = 0; j < matrix[0].length; j++) {
                    output += '─'.repeat(colWidths[j] + 2);
                    if(j < matrix[0].length - 1) output += '┼';
                }
                output += '┤\n';
            }
        }
        
        output += '└';
        for(let j = 0; j < matrix[0].length; j++) {
            output += '─'.repeat(colWidths[j] + 2);
            if(j < matrix[0].length - 1) output += '┴';
        }
        output += '┘\n';
        
        console.log(output);
    }
} 